﻿Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.Drawing
Imports System.ComponentModel
Imports System.Threading
Imports System.Windows

''' <summary>
''' A class which can make ImageAnimation,ColorAnimation,DoubleAnimation and IntegerAnimation.
''' </summary>
Public Class Animation

    Private Delegate Sub AccessValueDelegate(Of T)(ByVal PA As AnimationArgument(Of T), ByVal Value As T)
    Friend Shared Sub AccessValue(Of T)(ByVal PA As AnimationArgument(Of T), ByVal Value As T)
        If TypeOf PA.Member.ParentClass Is Control AndAlso PA.Member.ParentClass.InvokeRequired Then
            CType(PA.Member.ParentClass, Control).Invoke(New AccessValueDelegate(Of T)(AddressOf AccessValue(Of T)), PA, Value)
        Else
            Try
                CallByName(PA.Member.ParentClass, PA.Member.MemberName, CallType.Set, Value)
            Catch ex As ArgumentException
                If ex.Message <> "Destination array was not long enough. Check destIndex and length, and the array's lower bounds." Then
                    Throw ex
                Else
                    AccessValue(Of T)(PA, Value)
                End If
            End Try
        End If
    End Sub

#Region "Integer Animation"

    ''' <summary>
    ''' Create animation for Integer values.
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    ''' <param name="Duration">The duration of animation.</param>
    Public Sub IntegerAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Integer, ByVal Duration As Integer, Optional ByVal EachTimeChange As Integer = 1) ', Optional ByVal PropertyIndex As Object = Nothing)
        Dim Sm As New StringMember(Of Integer) With {.MemberName = MemberName, .ParentClass = ParentClass}
        If Sm.Value = ToValue OrElse Sm.Value - ToValue = 0 Then
            For i = 0 To Duration
                Thread.Sleep(1)
                Application.DoEvents()
            Next
            Exit Sub
        End If
        Dim EachTimeAdd As Integer = If(Sm.Value < ToValue, EachTimeChange, EachTimeChange * -1)
        Dim TotalTime As Integer
        If Sm.Value - ToValue = 0 Then
            Exit Sub
        End If
        Dim EachTime As Integer = Duration / ((If(Sm.Value < ToValue, ToValue - Sm.Value, Sm.Value - ToValue)) / EachTimeChange)
        If EachTime < 0 Then
            EachTime = 0
        End If
        Dim AG As New AnimationArgument(Of Integer)
        AG.Member = New StringMember(Of Integer) With {.MemberName = MemberName, .ParentClass = ParentClass}
        AG.Duration = Duration
        AG.ToValue = ToValue
        For i = Sm.Value + EachTimeAdd To ToValue Step EachTimeAdd
            Dim Diffr = If(Sm.Value < ToValue, (If(Sm.Value < ToValue, ToValue - Sm.Value, Sm.Value - ToValue)), (If(Sm.Value < ToValue, ToValue - Sm.Value, Sm.Value - ToValue)) * -1)
            Dim Cand As Boolean
            If EachTimeAdd < 0 Then
                Cand = Diffr > EachTimeAdd
            Else
                Cand = Diffr < EachTimeAdd
            End If
            If i + EachTimeAdd <> ToValue AndAlso Cand Then
                Sm.Value = ToValue
                Exit For
            End If
            If PlayAnimationWithAnyValue = False AndAlso CallByName(ParentClass, MemberName, CallType.Get) <> i - EachTimeAdd Then
                IntegerAnimation(ParentClass, MemberName, ToValue, Duration - TotalTime, EachTimeChange)
                Exit Sub
            End If
            AccessValue(Of Integer)(AG, i)
            Application.DoEvents()
            TotalTime += EachTime
            Threading.Thread.Sleep(EachTime)
        Next
    End Sub

    ''' <summary>
    ''' Create animation for Integer values that it play in background..
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    ''' <param name="Duration">The duration of animation.</param>
    '''  <param name="EachTimeChange" >Amount of changing value in each time.</param>
    ''' <param name="ReturnTimer" >This method use a timer to create an animation.You can give it to us or we created it automaticaly.</param>
    Public Function BackgroundIntegerAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Integer, ByVal Duration As Integer, Optional ByVal EachTimeChange As Integer = 1, Optional ByVal ReturnTimer As Forms.Timer = Nothing) As Forms.Timer
        Dim Sm As New StringMember(Of Integer) With {.MemberName = MemberName, .ParentClass = ParentClass}
        If Sm.Value = ToValue OrElse Sm.Value - ToValue = 0 Then
            Return New Forms.Timer
        End If
        Dim TotalTime As Integer
        Dim Diff As Integer = (If(Sm.Value < ToValue, ToValue - Sm.Value, Sm.Value - ToValue))
        Dim EachTime As Integer = Duration / (Diff / EachTimeChange)
        If EachTime <= 0 Then
            EachTime = 1
        End If
        Dim AG As New AnimationArgument(Of Integer)
        Dim EachTimeAdd As Integer = If(Sm.Value < ToValue, EachTimeChange, EachTimeChange * -1)
        AG.Member = Sm
        AG.Duration = Duration
        AG.ToValue = ToValue
        Dim TA As Forms.Timer
        If ReturnTimer Is Nothing Then
            TA = New Forms.Timer
        Else
            TA = ReturnTimer
        End If
        Dim i As Integer = Sm.Value + EachTimeAdd
        TA.Enabled = True
        TA.Interval = EachTime
        AddHandler TA.Tick, Sub(sender As Object, e As EventArgs)
                                Dim Diffr = If(Sm.Value < ToValue, (If(Sm.Value < ToValue, ToValue - Sm.Value, Sm.Value - ToValue)), (If(Sm.Value < ToValue, ToValue - Sm.Value, Sm.Value - ToValue)) * -1)
                                Dim Cand As Boolean
                                If EachTimeAdd < 0 Then
                                    Cand = Diffr > EachTimeAdd
                                Else
                                    Cand = Diffr < EachTimeAdd
                                End If
                                If i + EachTimeAdd <> ToValue AndAlso Cand Then
                                    TA.Dispose()
                                    Sm.Value = ToValue
                                    Exit Sub
                                End If
                                If i = ToValue Then
                                    TA.Dispose()
                                    Exit Sub
                                End If
                                If PlayAnimationWithAnyValue = False AndAlso CallByName(ParentClass, MemberName, CallType.Get) <> i - EachTimeAdd Then
                                    TA.Dispose()
                                    BackgroundIntegerAnimation(ParentClass, MemberName, ToValue, Duration - TotalTime, EachTimeChange, TA)
                                    Exit Sub
                                End If
                                Sm.Value = i
                                TotalTime += EachTime
                                i += EachTimeAdd
                            End Sub
        Return TA
    End Function
#End Region
    ''' <summary>
    ''' Create animation for Image values.
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    '''   <param name="EachStep" >Amount of changing value in each time.</param>
    ''' <param name="Duration">The duration of animation.
    ''' Warning: Duration may work badly when LoadFramesFirst is false.</param>
    ''' <param name="LoadFramesFirst" >If this is true,Animation's frames will be created first and then animation will be played.</param>
    ''' <param name="RunInBackground" >If this is true,Animation will be animated in background.
    ''' Important: When you run ImageAnimation in background,you can't get Frames from the returned value.You must wait for the background action to finish and then you can get Frames using Frames property in the Animation Class.
    ''' Warning: Frames property in Animation Class only stores the frames of the latest ImageAnimation.</param>
    ''' <param name="RemoveOldImageWhenFinish" >If this is true,the old image will be replaced with the new one when animation finishes.</param>
    Public Function ImageFadeAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Image, Optional ByVal Duration As Integer = 1000, Optional ByVal EachStep As Single = 0.05, Optional ByVal RemoveOldImageWhenFinish As Boolean = False, Optional ByVal LoadFramesFirst As Boolean = False, Optional ByVal RunInBackground As Boolean = True) As ImageAnimationReturn
        Try
            Dim Temp As Image = CallByName(ParentClass, MemberName, CallType.Get)
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Image.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, MemberName)
        End Try
        Dim Th As New Thread(AddressOf FadeImage)
        Dim Rt As New ImageAnimationReturn
        If RunInBackground Then
            Rt.BackgroundThread = Th
        End If
        If CallByName(ParentClass, MemberName, CallType.Get) Is ToValue Then
            If RunInBackground = False Then
                Rt.Frames = New List(Of Image)
                Rt.Frames.Add(ToValue)
            End If
            Return Rt
        End If
        Dim AG As New ImageArgument
        AG.Member = New StringMember(Of Image) With {.MemberName = MemberName, .ParentClass = ParentClass}
        AG.EachStep = EachStep
        AG.ToValue = ToValue
        AG.Duration = Duration
        AG.RemoveOldImageWhenFinish = RemoveOldImageWhenFinish
        AG.LoadFramesFirst = LoadFramesFirst
        If RunInBackground Then
            Th.Start(AG)
        Else
            Rt.Frames = FadeImage(AG)
        End If
        Return Rt
    End Function

    ''' <summary>
    ''' Gets or sets frames of the latest ImageAnimation.
    ''' </summary>
    Public Property Frames As List(Of Image)

    Private Function FadeImage(ByVal AG As ImageArgument) As List(Of Image)
        Dim current_alpha As Single = 0.0
        Dim increase As Boolean = True
        Dim i1 As Bitmap
        Dim i2 As New Bitmap(AG.ToValue)
        Dim i3 As Bitmap
        Dim Frames As New List(Of Image)
        Dim Dr As Integer = AG.Duration / (1 / AG.EachStep)

1:
        Try
            i1 = AG.Member.Value.Clone
        Catch ex As InvalidOperationException
            GoTo 1
        End Try



        While True
            i3 = i1.Clone
            Dim g As Graphics = Graphics.FromImage(i3)
            Dim cm As New Imaging.ColorMatrix(New Single()() { _
                                                             New Single() {1, 0, 0, 0, 0}, _
                                                             New Single() {0, 1, 0, 0, 0}, _
                                                             New Single() {0, 0, 1, 0, 0}, _
                                                             New Single() {0, 0, 0, current_alpha, 0}, _
                                                             New Single() {0, 0, 0, 0, 1}})

            Dim ia As New Imaging.ImageAttributes
            ia.SetColorMatrix(cm, Imaging.ColorMatrixFlag.Default, Imaging.ColorAdjustType.Bitmap)

            g.DrawImage(i2, New Rectangle(0, 0, i3.Width, i3.Height), 0, 0, i2.Width, i2.Height, GraphicsUnit.Pixel, ia) '
            ' g.DrawImage(i2, New Rectangle(0, 0, i2.Width, i2.Height), 0, 0, i2.Width, i2.Height, GraphicsUnit.Pixel, ia)
            g.Dispose()

            If increase Then
                current_alpha += AG.EachStep
            Else
                current_alpha -= AG.EachStep
            End If
            Frames.Add(i3)
            If AG.LoadFramesFirst = False Then
                AG.Member.Value = i3
            End If
            If current_alpha >= 1 Then
                current_alpha = 1
                Exit While
            ElseIf current_alpha <= 0 Then
                current_alpha = 0
                Exit While
            End If
            Thread.Sleep(Dr)
        End While
        If AG.LoadFramesFirst Then
            For Each Frame In Frames
                AG.Member.Value = Frame
                Thread.Sleep(Dr)
            Next
        End If
        If AG.RemoveOldImageWhenFinish Then
            AG.Member.Value = AG.ToValue
        End If
        Me.Frames = Frames
        Return Frames
    End Function

    ''' <summary>
    ''' Create animation for Double values.
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    ''' <param name="Duration">The duration of animation.</param>
    '''  <param name="EachStep" >Amount of changing value in each time.</param>
    Public Sub DoubleAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Double, ByVal Duration As Integer, Optional ByVal EachStep As Double = 0.001, Optional ByVal CheckPlayingAttention As Integer = 4) ', Optional ByVal PropertyIndex As Object = Nothing)
        Try
            Dim Temp As Double = CallByName(ParentClass, MemberName, CallType.Get)
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Double.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, MemberName)
        End Try
        If CallByName(ParentClass, MemberName, CallType.Get) = ToValue Then
            For i = 0 To Duration
                Thread.Sleep(1)
                Application.DoEvents()
            Next
            Exit Sub
        End If
        Dim TotalTime As Double
        Dim sourse As Double = CallByName(ParentClass, MemberName, CallType.Get)
        Dim NumOfZeros As Integer = 10 ^ (Str(EachStep).Substring(Str(EachStep).LastIndexOf(".") + 1).Length)
        If sourse - ToValue = 0 Then
            Exit Sub
        End If
        Dim EachTime As Double = Duration / (If(sourse < ToValue, (ToValue - sourse) * NumOfZeros, (sourse - ToValue) * NumOfZeros))
        Dim AddStep As Double = If(sourse < ToValue, EachStep, -EachStep)
        Dim AG As New AnimationArgument(Of Double)
        AG.Member = New StringMember(Of Double) With {.MemberName = MemberName, .ParentClass = ParentClass}
        AG.Duration = Duration
        AG.ToValue = ToValue
        For i = sourse + AddStep To ToValue Step AddStep
            If PlayAnimationWithAnyValue = False AndAlso FormatNumber(CallByName(ParentClass, MemberName, CallType.Get), CheckPlayingAttention) <> FormatNumber(i - AddStep, CheckPlayingAttention) Then
                DoubleAnimation(ParentClass, MemberName, ToValue, Duration - TotalTime, EachStep, CheckPlayingAttention)
            End If
            AccessValue(Of Double)(AG, i)
            Application.DoEvents()
            TotalTime += EachTime
            Threading.Thread.Sleep(EachTime)
        Next
        AccessValue(Of Double)(AG, ToValue)
    End Sub
#Region "Color Animation"

    ''' <summary>
    ''' Create animation for Color values.
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    ''' <param name="Duration">The duration of animation.</param>
    '''  <param name="EachTimeChange" >Amount of changing value in each time.</param>
    ''' <param name="AbortAllThreads" >If true,all threads that have been used in animation, will abort.</param>
    Public Function ColorAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Color, ByVal Duration As Integer, Optional ByVal EachTimeChange As Integer = 1, Optional ByVal AbortAllThreads As Boolean = False) As List(Of Thread)
        Try
            Dim Temp As Color = CallByName(ParentClass, MemberName, CallType.Get)
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Color.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, MemberName)
        End Try
        If CallByName(ParentClass, MemberName, CallType.Get) = ToValue Then
            Return New List(Of Thread)
        End If
        Dim Cco As New ChangingColor
        Cco.ParentClass = ParentClass
        Cco.MemberName = MemberName
        Dim CA As New ColorArgument
        CA.Cco = Cco
        CA.Duration = Duration
        CA.ToValue = ToValue
        CA.EachTimeChange = EachTimeChange
        Dim TA As New Thread(AddressOf Alpha)
        TA.Start(CA)
        Dim TR As New Thread(AddressOf Red)
        TR.Start(CA)
        Dim TG As New Thread(AddressOf Green)
        TG.Start(CA)
        Dim TB As New Thread(AddressOf Blue)
        TB.Start(CA)
        For i = 0 To Duration Step 1
            Application.DoEvents()
            Threading.Thread.Sleep(1)
        Next
        Dim Threads As New List(Of Thread)
        Threads.Add(TA)
        Threads.Add(TR)
        Threads.Add(TB)
        Threads.Add(TG)
        If AbortAllThreads Then
            AbortAll(Threads)
        End If
        Return Threads
    End Function
    Private Sub AbortAll(ByVal Threads As List(Of Thread))
        For i = 0 To Threads.Count - 1
            Threads(i).Abort()
        Next
    End Sub
    ''' <summary>
    ''' Create animation for Color values that it play in background.
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    ''' <param name="Duration">The duration of animation.</param>
    '''  <param name="EachTimeChange" >Amount of changing value in each time.</param>
    Public Function BackgroundColorAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Color, ByVal Duration As Integer, Optional ByVal EachTimeChange As Integer = 1) As List(Of Forms.Timer)
        Dim Cco As New ChangingColor
        Cco.ParentClass = ParentClass
        Cco.MemberName = MemberName
        Dim Timers As New List(Of Forms.Timer)
        Timers.Add(BackgroundIntegerAnimation(Cco, "A", ToValue.A, Duration, EachTimeChange))
        Timers.Add(BackgroundIntegerAnimation(Cco, "B", ToValue.B, Duration, EachTimeChange))
        Timers.Add(BackgroundIntegerAnimation(Cco, "G", ToValue.G, Duration, EachTimeChange))
        Timers.Add(BackgroundIntegerAnimation(Cco, "R", ToValue.R, Duration, EachTimeChange))
        Return Timers
    End Function
    Private Sub ColorA(ByVal Argument As ColorArgument)
        ColorAnimation(Argument.Cco.ParentClass, Argument.Cco.MemberName, Argument.ToValue, Argument.Duration)
    End Sub
    ''' <summary>
    ''' Gets or sets indicates whether animations force change the value of sourse.
    ''' If be false,Animations creators macth them with new value of sourse.
    ''' </summary>
    Public Property PlayAnimationWithAnyValue As Boolean = False
    Private Sub Blue(ByVal e As Object)
        Dim CA As ColorArgument = e
        IntegerAnimation(CA.Cco, "B", CA.ToValue.B, CA.Duration, CA.EachTimeChange)
    End Sub
    Private Sub Green(ByVal e As Object)
        Dim CA As ColorArgument = e
        IntegerAnimation(CA.Cco, "G", CA.ToValue.G, CA.Duration, CA.EachTimeChange)
    End Sub
    Private Sub Red(ByVal e As Object)
        Dim CA As ColorArgument = e
        IntegerAnimation(CA.Cco, "R", CA.ToValue.R, CA.Duration, CA.EachTimeChange)
    End Sub
    Private Sub Alpha(ByVal e As Object)
        Dim CA As ColorArgument = e
        IntegerAnimation(CA.Cco, "A", CA.ToValue.A, CA.Duration, CA.EachTimeChange)
    End Sub

#End Region
#Region "Point Animation"

    ''' <summary>
    ''' Create animation for Point values.
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    ''' <param name="Duration">The duration of animation.</param>
    ''' <remarks >Don't set duration with small values.</remarks>
    Private Sub PointAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Point, ByVal Duration As Integer)
        Try
            Dim Temp As Point = CallByName(ParentClass, MemberName, CallType.Get)
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Point.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, MemberName)
        End Try
        If CallByName(ParentClass, MemberName, CallType.Get) = ToValue Then
            For i = 0 To Duration
                Thread.Sleep(1)
                Application.DoEvents()
            Next
            Exit Sub
        End If
        Dim XThread As New Thread(AddressOf Y)
        Dim PA As New PointArgument
        PA.Member = New StringMember(Of Point) With {.MemberName = MemberName, .ParentClass = ParentClass}
        PA.Duration = Duration
        PA.ToValue = ToValue
        XThread.Start(PA)
        X(PA)
    End Sub
    Private Sub X(ByVal PA As PointArgument)
        Dim t As Point = CallByName(PA.Member.ParentClass, PA.Member.MemberName, CallType.Get)
        XPointAnimation(t, PA.ToValue.X, PA.Duration, PA)
    End Sub
    Private Sub XPointAnimation(ByRef ParentClass As Point, ByVal ToValue As Integer, ByVal Duration As Integer, ByVal PA As PointArgument)
        Try
            Dim Temp As Integer = ParentClass.X
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Integer.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, "X")
        End Try
        If ParentClass.X = ToValue Then
            For i = 0 To Duration
                Thread.Sleep(1)
                Application.DoEvents()
            Next
            Exit Sub
        End If
        Dim TotalTime As Integer
        Dim sourse As Integer = ParentClass.X
        Dim EachTime As Integer = Duration / (If(sourse < ToValue, ToValue - sourse, sourse - ToValue))
        If EachTime < 0 Then
            EachTime = 0
        End If
        For i = sourse + If(sourse < ToValue, 1, -1) To ToValue Step If(sourse < ToValue, 1, -1)
            If PlayAnimationWithAnyValue = False AndAlso ParentClass.X <> i - If(sourse < ToValue, 1, -1) Then
                XPointAnimation(ParentClass, ToValue, Duration - TotalTime, PA)
                Exit Sub
            End If
            ParentClass.X = i
            AccessControl(PA, New Point(i, CallByName(PA.Member.ParentClass, PA.Member.MemberName, CallType.Get).Y))
            Application.DoEvents()
            TotalTime += EachTime
            Threading.Thread.Sleep(EachTime)
        Next
    End Sub
    Private Sub Y(ByVal PA As PointArgument)
        Dim t As Point = CallByName(PA.Member.ParentClass, PA.Member.MemberName, CallType.Get)
        YPointAnimation(t, PA.ToValue.Y, PA.Duration, PA)
    End Sub
    Private Sub YPointAnimation(ByRef ParentClass As Point, ByVal ToValue As Integer, ByVal Duration As Integer, ByVal PA As PointArgument)
        Try
            Dim Temp As Integer = ParentClass.Y
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Integer.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, "Y")
        End Try
        If ParentClass.Y = ToValue Then
            For i = 0 To Duration
                Thread.Sleep(1)
                Application.DoEvents()
            Next
            Exit Sub
        End If
        Dim TotalTime As Integer
        Dim sourse As Integer = ParentClass.Y
        Dim EachTime As Integer = Duration / (If(sourse < ToValue, ToValue - sourse, sourse - ToValue))
        If EachTime < 0 Then
            EachTime = 0
        End If
        For i = sourse + If(sourse < ToValue, 1, -1) To ToValue Step If(sourse < ToValue, 1, -1)
            If PlayAnimationWithAnyValue = False AndAlso ParentClass.Y <> i - If(sourse < ToValue, 1, -1) Then
                YPointAnimation(ParentClass, ToValue, Duration - TotalTime, PA)
                Exit Sub
            End If
            ParentClass.Y = i
            AccessControl(PA, New Point(CallByName(PA.Member.ParentClass, PA.Member.MemberName, CallType.Get).x, i))
            Application.DoEvents()
            TotalTime += EachTime
            Threading.Thread.Sleep(EachTime)
        Next
    End Sub
    Private Delegate Sub PointAccess(ByVal PA As PointArgument, ByVal Value As Point)
    Private Sub AccessControl(ByVal PA As PointArgument, ByVal Value As Point)
        If TypeOf PA.Member.ParentClass Is Control AndAlso PA.Member.ParentClass.InvokeRequired Then
            CType(PA.Member.ParentClass, Control).Invoke(New PointAccess(AddressOf AccessControl), PA, Value)
        Else
            CallByName(PA.Member.ParentClass, PA.Member.MemberName, CallType.Set, Value)
        End If
    End Sub

#End Region
#Region "Size Animation"

    ''' <summary>
    ''' Create animation for Size values.
    ''' </summary>
    ''' <param name="ParentClass">A reference of the object exposing a member. </param>
    ''' <param name="MemberName">Name of the member that animation will play on it.</param>
    ''' <param name="ToValue">A value to change member to it.</param>
    ''' <param name="Duration">The duration of animation.</param>
    ''' <param name="RunInBackground">If this is true,Animation will be animated in background.</param>
    Public Sub SizeAnimation(ByVal ParentClass As Object, ByVal MemberName As String, ByVal ToValue As Size, ByVal Duration As Integer, Optional ByVal RunInBackground As Boolean = True)
        Try
            Dim Temp As Size = CallByName(ParentClass, MemberName, CallType.Get)
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Size.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, MemberName)
        End Try
        If CallByName(ParentClass, MemberName, CallType.Get) = ToValue Then
            Exit Sub
        End If
        Dim PA As New SizeArgument
        PA.Member = New SizeMember With {.MemberName = MemberName, .ParentClass = ParentClass}
        PA.Duration = Duration
        PA.ToValue = ToValue
        Dim XThread As New Thread(AddressOf Height)

        XThread.Start(PA)
        Dim WThread As New Thread(AddressOf Width)
        WThread.Start(PA)
        If RunInBackground = False Then
            For i = 0 To Duration Step 1
                Application.DoEvents()
                Threading.Thread.Sleep(1)
            Next
        End If
    End Sub
    Private Sub Width(ByVal PA As SizeArgument)
        IntegerAnimation(PA.Member, "Width", PA.ToValue.Width, PA.Duration)
        Exit Sub
        XSizeAnimation(PA.Member, PA.ToValue.Width, PA.Duration, PA)
    End Sub
    Private Sub XSizeAnimation(ByVal Member As SizeMember, ByVal ToValue As Integer, ByVal Duration As Integer, ByVal PA As SizeArgument)
        Try
            Dim Temp As Integer = Member.Value.Width
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Integer.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(Member.Value.GetType().Name, "Width")
        End Try
        If Member.Value.Width = ToValue Then
            Exit Sub
        End If
        Dim TotalTime As Integer
        Dim sourse As Integer = Member.Value.Width
        Dim EachTime As Integer = Duration / (If(sourse < ToValue, ToValue - sourse, sourse - ToValue))
        If EachTime < 0 Then
            EachTime = 0
        End If
        For i = sourse + If(sourse < ToValue, 1, -1) To ToValue Step If(sourse < ToValue, 1, -1)
            If PlayAnimationWithAnyValue = False AndAlso Member.Value.Width <> i - If(sourse < ToValue, 1, -1) Then
                XSizeAnimation(Member, ToValue, Duration - TotalTime, PA)
                Exit Sub
            End If
            AccessSize(PA, New Size(i, Member.Value.Height))
            Application.DoEvents()
            TotalTime += EachTime
            Threading.Thread.Sleep(EachTime)
        Next
    End Sub
    Private Sub Height(ByVal PA As SizeArgument)
        IntegerAnimation(PA.Member, "Height", PA.ToValue.Height, PA.Duration)
        Exit Sub
        YSizeAnimation(PA.Member, PA.ToValue.Height, PA.Duration, PA)
    End Sub
    Private Sub YSizeAnimation(ByRef Member As SizeMember, ByVal ToValue As Integer, ByVal Duration As Integer, ByVal PA As SizeArgument)
        Try
            Dim Temp As Integer = Member.Value.Height
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a  Integer.")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(Member.Value.GetType().Name, "Height")
        End Try
        If Member.Value.Height = ToValue Then
            Exit Sub
        End If
        Dim TotalTime As Integer
        Dim sourse As Integer = Member.Value.Height
        Dim EachTime As Integer = Duration / (If(sourse < ToValue, ToValue - sourse, sourse - ToValue))
        If EachTime < 0 Then
            EachTime = 0
        End If
        For i = sourse + If(sourse < ToValue, 1, -1) To ToValue Step If(sourse < ToValue, 1, -1)
            If PlayAnimationWithAnyValue = False AndAlso Member.Value.Height <> i - If(sourse < ToValue, 1, -1) Then
                YSizeAnimation(Member, ToValue, Duration - TotalTime, PA)
                Exit Sub
            End If
            AccessSize(PA, New Size(Member.Value.Width, i))
            Application.DoEvents()
            TotalTime += EachTime
            Threading.Thread.Sleep(EachTime)
        Next
    End Sub
    Private Delegate Sub SizeAccess(ByVal PA As SizeArgument, ByVal Value As Size)
    Private Sub AccessSize(ByVal PA As SizeArgument, ByVal Value As Size)
        If TypeOf PA.Member.ParentClass Is Control AndAlso PA.Member.ParentClass.InvokeRequired Then
            CType(PA.Member.ParentClass, Control).Invoke(New SizeAccess(AddressOf AccessSize), PA, Value)
        Else
            PA.Member.Value = Value
            'CallByName(PA.Member.ParentClass, PA.Member.MemberName, CallType.Set, Value)
        End If
    End Sub
#End Region
End Class
''' <summary>
''' This is used by ImageAnimation method to return Frames and Background thread.
''' </summary>
''' <remarks></remarks>
Public Class ImageAnimationReturn
    ''' <summary>
    ''' Gets or sets background thread of an ImageAnimation.
    ''' </summary>
    Public Property BackgroundThread As Thread

    Dim Framesv As List(Of Image)
    ''' <summary>
    ''' Gets or sets frames of an ImageAnimation.
    ''' </summary>
    Public Property Frames As List(Of Image)
        Get
            If Framesv Is Nothing Then
                Throw New InvalidOperationException("When you run ImageAnimation in background,you can't get Frames from the returned value.You must wait for the background action to finish and then you can get Frames using Frames property in the Animation Class." + vbNewLine + "Important: Frames property in Animation Class only stores the frames of the latest ImageAnimation.")
            End If
            Return Framesv
        End Get
        Set(ByVal value As List(Of Image))
            Framesv = value
        End Set
    End Property
End Class

Friend Class PointArgument
    Inherits AnimationArgument(Of Point)
End Class
Friend Class SizeArgument
    Public Property ToValue As Size
    Public Property Duration As Integer
    Public Property Member As SizeMember
End Class
Friend Class AnimationArgument(Of T)
    Public Property ToValue As T
    Public Property Duration As Integer
    Public Property Member As StringMember(Of T)
End Class
Friend Class ImageArgument
    Inherits AnimationArgument(Of Image)
    Public Property EachStep As Double
    Public Property LoadFramesFirst As Boolean
    Public Property RemoveOldImageWhenFinish As Boolean
End Class
Friend Class ChangingColor
    Public ParentClass As Object
    Public MemberName As String
    Public Property Color As Color
        Get
            Return CallByName(ParentClass, MemberName, CallType.Get)
        End Get
        Set(ByVal value As Color)
            CallByName(ParentClass, MemberName, CallType.Set, value)
        End Set
    End Property
    Public Property A As Integer
        Get
            Return Color.A
        End Get
        Set(ByVal value As Integer)
            CallByName(ParentClass, MemberName, CallType.Set, Color.FromArgb(value, Color))
        End Set
    End Property
    Public Property R As Integer
        Get
            Return Color.R
        End Get
        Set(ByVal value As Integer)
            With Color
                CallByName(ParentClass, MemberName, CallType.Set, Color.FromArgb(.A, value, .G, .B))
            End With
        End Set
    End Property
    Public Property G As Integer
        Get
            Return Color.G
        End Get
        Set(ByVal value As Integer)
            With Color
                CallByName(ParentClass, MemberName, CallType.Set, Color.FromArgb(.A, .R, value, .B))
            End With
        End Set
    End Property
    Public Property B As Integer
        Get
            Return Color.B
        End Get
        Set(ByVal value As Integer)
            With Color
                CallByName(ParentClass, MemberName, CallType.Set, Color.FromArgb(.A, .R, .G, value))
            End With
        End Set
    End Property
End Class
Friend Class ColorArgument
    Public Property ToValue As Color
    Public Property Cco As ChangingColor
    Public Property Duration As Integer
    Public Property EachTimeChange As Integer
End Class
''' <summary>
''' Gets or sets a member of a class with name of it.
''' </summary>
Public Class StringMember
    Inherits StringMember(Of Object)
End Class
''' <summary>
''' Gets or sets a member of a class with name of it.
''' </summary>
''' <typeparam name="T">Type of member.</typeparam>
Public Class StringMember(Of T)
    ''' <summary>
    ''' Gets or sets a class that has this member.
    ''' </summary>
    Public Property ParentClass As Object
    ''' <summary>
    ''' Gets or sets name of this member.
    ''' </summary>
    Public Property MemberName As String
    ''' <summary>
    ''' Gets or sets this member.
    ''' </summary>
    Public Property Value As T
        Get
            Return GetValue()
        End Get
        Set(ByVal SetValue As T)
            GetValue()
            Try
                ' CallByName(ParentClass, MemberName, CallType.Set, value) 
                Animation.AccessValue(Of T)(New AnimationArgument(Of T) With {.Member = New StringMember(Of T) With {.MemberName = MemberName, .ParentClass = ParentClass}}, SetValue)
            Catch ex As ArgumentException
                If ex.Message <> "Destination array was not long enough. Check destIndex and length, and the array's lower bounds." Then
                    Throw ex
                Else
                    SetMyValue(SetValue)
                End If
            End Try
        End Set
    End Property
    Private Sub SetMyValue(ByVal SetValue As T)
        Value = SetValue
    End Sub

    Private Function GetValue() As T
        Try
            Return CallByName(ParentClass, MemberName, CallType.Get)
        Catch ex As InvalidCastException
            Throw New InvalidCastException("The type of member (It got from MemberName) must be a " + GetType(T).Name + ".")
        Catch ex As MissingMemberException
            Throw New MissingMemberException(ParentClass.GetType().Name, MemberName)
        End Try
    End Function
End Class
Friend Class SizeMember
    Inherits StringMember(Of Size)
    Public Property Width As Integer
        Get
            Return Value.Width
        End Get
        Set(ByVal Number As Integer)
            Value = New Size(Height, Number)
        End Set
    End Property
    Public Property Height As Integer
        Get
            Return Value.Height
        End Get
        Set(ByVal Number As Integer)
            Value = New Size(Number, Width)
        End Set
    End Property
End Class
Friend Module SharedMethods
    'Only for fields
    Public Function CallByName(ByVal ObjectRef As Object, ByVal ProcName As String, ByVal UseCallType As Microsoft.VisualBasic.CallType, Optional ByVal SetValue As Object = Nothing) As Object
        Dim RefMem = ObjectRef.GetType().GetField(ProcName, Reflection.BindingFlags.Static Or Reflection.BindingFlags.FlattenHierarchy Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
        If RefMem Is Nothing Then
            Dim RefMem2 = ObjectRef.GetType().GetProperty(ProcName, Reflection.BindingFlags.Static Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.FlattenHierarchy)
            If UseCallType = CallType.Get Then
                Return RefMem2.GetValue(ObjectRef, Nothing)
            ElseIf UseCallType = CallType.Set Then
                RefMem2.SetValue(ObjectRef, SetValue, Nothing)
            End If
        Else
            If UseCallType = CallType.Get Then
                Return RefMem.GetValue(ObjectRef)
            ElseIf UseCallType = CallType.Set Then
                RefMem.SetValue(ObjectRef, SetValue)
            End If
        End If
        Return Nothing
    End Function
End Module

